/*
 * pn_hl.cpp
 *
 *  Created on: 09.05.2013
 *      Author: Roma Jam
 */

#include "pn_hl.h"
#include "pn_lld.h"
#include "card.h"
#include "led_indicate.h"

Pn_t Pn;

// ============================= Implementation ================================
void Pn_t::Task() {
    uint32_t ReplyLen=0;
    Pn_PktRslt_t Rpl;

    switch(Card.State) {
        case csHwFail:
//            // Reset everything
            CrdPwrOff();
            Uart.Printf("csHwFail\r");
            Init();
            break;

        case csCmdFail:
//            Card.State = csIdle;
            break;

        case csOut:
            if(Delay.Elapsed(&ITimer, NEW_CRD_SEARCH_DELAY)) {
                if(CardIsAppeared()) {
                    Led.SetColor(lcBlue);
//                    Ccid.NotifySlotStatus(SLOT0_CHANGED + SLOT0_ICC_PRESENT);
                    Card.State = csIdle;
                    Card.ConfigATR(Pn_lvl1.Buf, Pn_lvl1.PktLen);
                    Uart.Printf("csIdle\r");
                }
            } // delay
            break;

        case csIdle:
            if(Delay.Elapsed(&ITimer, CRD_STILL_IN_CHECK_DELAY)) {
                if(CheckCardIsStillNear() != OK) {
                    Card.State = csOut;
                    CrdPwrOff();
                    Led.SetColor(lcOrange);
                    Uart.Printf("cs Out\r");
//
                }
            } // if delay
            break;
//
        case csBusy:
            if(Delay.Elapsed(&ITimer, CRD_TRANSACTION_TIMEOUT)) {
                Card.State = csCmdFail;
                Uart.Printf("Tmt\r");
//                Ccid.DataReplyCallBack(0, 0);
            }
            else {  // Check if data ready
                if(Pn_lvl1.Reply_Is_Ready()) { // Some reply is ready
                    Led.Blink();
                    Rpl = Pn_lvl1.ReadPkt(Card.Buf, &ReplyLen);
                    switch(Rpl) {
                        case pnPktOk:
                            if (Pn_lvl1.Buf[1] == 0x00) {
                                Card.State = csIdle;
                                ReplyLen -= Pn_lvl1.PktLen;
//                                Ccid.DataReplyCallBack(Card.Buf, ReplyLen);
                            }
                            else {
                                Card.State = csCmdFail;
                                Uart.Printf("csCmdFail\r");
//                                Ccid.DataReplyCallBack(0, 0);
                            }
                            break;
//
                        case pnPktErrFrame: // Card said something baaad
                            Card.State = csCmdFail;
                            Uart.Printf("csCmdFail\r");
//                            Ccid.DataReplyCallBack(0, 0);
                            break;
//
                        default:
                            Card.State = csHwFail;
                            Uart.Printf("hwFail: %X\r", (uint8_t)Rpl);
                            Led.SetColor(lcOrange);
//                            Ccid.DataReplyCallBack(0, 0);
                            break;
                    } // switch
                } // if reply is ready
                else {
                    // Send NTX
                    if(Delay.Elapsed(&CardTimer, 999)) {
                        Led.Blink();
//                        Ccid.NotifyTimeExtension(16);
                        Uart.Printf("Ntx\r");
                    }
                }
            } // if not timeout
            break;
    } // switch
}


void Pn_t::Init() {
    // ==== HW Init ====
    Pn_lvl1.Init();

    Pn_PktRslt_t Rslt;
    do {
        Pn_lvl1.Rst.Lo();
        Delay.ms(252);
        Uart.Printf("PnInit\r");
        Pn_lvl1.Rst.Hi();  // Remove reset
        Delay.ms(8);

        // Initial commands
        // First Cmd will be discarded
        Pn_lvl1.Cmd(PN_CMD_GET_FIRMWARE_VERSION, 0);
        Rslt = Pn_lvl1.Cmd(PN_CMD_SAM_CONFIGURATION, 1, 0x01); // Disable SAM to calm PN: Normal mode, the SAM is not used
        if(Rslt != pnPktOk) continue;
        Rslt = Pn_lvl1.Cmd(PN_CMD_RF_CONFIGURATION, 4, 0x05, 0x02, 0x01, 0x05);  // Setup RF config: One retry, ATR: once, PSL: once, Activation: once// Field On
    } while(Rslt != pnPktOk);
    Uart.Printf("PnOk\r");

    // PN Software Init
    PnType = pnct_Type_A_106kbps;
    DataTimeout = DATA_TIMEOUT_JCOP;
}


void Pn_t::CrdPwrOff() {
    FieldOff();
    Card.State = csOut;
    Led.SetColor(lcRed);
    Card.ClearATR();
//    Ccid.NotifySlotStatus(SLOT0_CHANGED + SLOT0_ICC_ABSENT);
    Uart.Printf("CrdPwrOff\r");
}


/*
 * Check if new cards appeared
 */
bool Pn_t::CardIsAppeared() {
    // Check if something is near
    FieldOn();
    if(PnType & pnct_Type_A_106kbps) {   // type A 106 kbps
//        klPrintf("typeA\r");
        // Max number of targets = 1, Baudrate: 106kbps type A
        if(Pn_lvl1.Cmd(PN_CMD_IN_LIST_PASSIVE_TARGET, 2, 0x02, 0x00) != pnPktOk) { FieldOff(); return false; }
        if (Pn_lvl1.Buf[0] != PN_RPL_IN_LIST_PASSIVE_TARGET)                     { FieldOff(); return false; }
//        klPrintf("Tag: %A\r", Buf, Length);
        if (Pn_lvl1.Buf[1] == 0x02) {
//            klPrintf("Second is deselect\r");
            Pn_lvl1.Cmd(PN_CMD_IN_DESELECT, 1, 0x02);
            return true;
        }
        if (Pn_lvl1.Buf[1] == 0x01) {
//            klPrintf("Tag: %A\r", Buf, Length);
            return true;
        }
    }
    if(PnType & pnct_Type_B_106kbps) {   // type B 106 kbps
//        klPrintf("typeB\r");
        // Max number of targets = 1, Baudrate: 106kbps type B with default AFI
        if (Pn_lvl1.Cmd(PN_CMD_IN_LIST_PASSIVE_TARGET, 3, 0x01, 0x03, 0x00) != pnPktOk) { FieldOff(); return false; }
        if ((Pn_lvl1.Buf[0] != PN_RPL_IN_LIST_PASSIVE_TARGET) or (Pn_lvl1.Buf[1] == 0))         { FieldOff(); return false; }
        if (Pn_lvl1.Buf[1] == 0x01) {
//            klPrintf("Tag: %A\r", Buf, Length);
            return true;
        }
    }
    if(PnType & pnct_FeliCa_212kbps){   // FeliCa 212 kbps
//        klPrintf("FeliCa 212\r");
    }
    if(PnType & pnct_FeliCa_424kbps){   // FeliCa 424 kbps
//        klPrintf("FeliCa 424\r");
    }
    if(PnType & pnct_Jewel_106kbps){    // Jewel 106 kbps
//        klPrintf("Jewel\r");
    }
    // Nothing found field off and start again
    FieldOff();
    return false;
}

uint8_t Pn_t::CheckCardIsStillNear() {
    switch (Card.Type) {
        case ctMifare:
            if (Pn_lvl1.Cmd(PN_CMD_IN_DESELECT, 1, 1) == pnPktOk) {
                if (Pn_lvl1.Cmd(PN_CMD_IN_SELECT, 1, 1) == pnPktOk) {
                    if (Pn_lvl1.Buf[0] == PN_RPL_IN_SELECT) {
//                        klPrintf("Select: %X\r", Buf[1]);
                        if(Pn_lvl1.Buf[1] == 0) return OK;
//                            return (pncsPresentAndActive);
                    } // if rpl select
                } // if pkt select
            } // if deselect
            break;
        case ctJCop:
            if(Pn_lvl1.Cmd(PN_CMD_DIAGNOSE, 1, 0x06) == pnPktOk) {
//                klPrintf("Diag: %X\r", Buf[1]);
                if(Pn_lvl1.Buf[1] == 0)     return OK;
            } // if rpl diagnose
            else {
#ifdef PRINT_DIAGNOSE
                klPrintf("Diag Err: %X\r", Buf[1]);
#endif
            }
            break;
            //FIXME
//        case ctInfineon:
//            if(Cmd(PN_CMD_IN_DESELECT, 1, 1) == pnPktOk) {
//                klPrintf("Des: %X ", Buf[1]);
//                    if(Buf[1] == 0x00){
//                        if(Cmd(PN_CMD_IN_SELECT, 1, 1) == pnPktOk){
//                            return true;
//                        }
//                    }
//            } // if deselect
//            break;
        default: break;
    }
//    klPrintf("Nothing left near - FieldOff\r");
//    klPrintf("Buf: %A\r", Buf, Length);

#ifdef CARD_ANSW_PRINT
    switch (Buf[1]) {
        case 0x01:
            klPrintf("Er: Time Out\r");
            break;
        case 0x02:
            klPrintf("Er: CRC\r");
            break;
        case 0x03:
            klPrintf("Er: Parity\r");
            break;
        case 0x04:
            klPrintf("Er: Anticolision\r");
            break;
        case 0x05:
            klPrintf("Er: Mifare\r");
            break;
        case 0x06:
            klPrintf("Er: Bit Colision\r");
            break;
        case 0x07:
            klPrintf("Er: Comm Buf\r");
            break;
        case 0x09:
            klPrintf("Er: Buf Ovf\r");
            break;
        case 0x13:
            klPrintf("Er: Data Format\r");
            if(Cmd(PN_CMD_IN_JUMP_FOR_DEP, 1, 1) == pnPktOk){
                klPrintf("InDEP: %A\r", Buf, Length);
            } else klPrintf("sd failure\r");
        default:
            break;
    }
#endif

    Pn_lvl1.Cmd(PN_CMD_IN_RELEASE, 1, 1);
    FieldOff();
    return FAILURE;
}
